    
namespace SharePointExporter.FileSystem
{
    using System;
    using System.Globalization;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Security.Principal;
    using DSOFile;
    using Microsoft.Win32.Security;
    using SharePoint.ImportExport.Core;
//    using System.Diagnostics;

    /// <summary>
    /// Summary description for FileExporter.
    /// </summary>
    /// 

    public class FileExporter
    {
        private StreamWriter createdFiles;
        int nrOfFilesExported = 0;
        public FileExporter()
        {
            //
            // TODO: Add constructor logic here
            //
        }
        public bool CreateContentTypes(FileParams p)
        {

            bool returnValue = true;
            #region Create ListOfItems Template
            try
            {
                ListOfContentTypes ContentTypes = new ListOfContentTypes();
                if (createdFiles == null)
                    createdFiles = new StreamWriter(p.OutputDirectory + "\\deletecreatedfiles.cmd");

                ContentTypeDefinition contentType = new ContentTypeDefinition();

                ContentTypes.ContentTypesCollection.Add(contentType);
                contentType.NameAttribute = "Imported Documents";
                contentType.Name = "Imported Documents";
                contentType.Title = "Imported Documents";
                contentType.Description = "Template created for File System Export";
                contentType.BaseType = "DocumentLibrary";
                contentType.EnableVersioning = false;
                contentType.EnableAttachments = false;

                Field field = new Field();
                field.AddToDefaultView = true;
                field.DisplayName = "File Author";
                field.ExtendedType = FieldType.Text;
                field.Name = "File Author";
                field.Required = false;
                field.Richtext = false;
                contentType.Fields.Add(field);

                field = new Field();
                field.AddToDefaultView = false;
                field.DisplayName = "File Description";
                field.ExtendedType = FieldType.Text;
                field.Name = "File Description";
                field.Required = false;
                field.Richtext = false;
                contentType.Fields.Add(field);

                field = new Field();
                field.AddToDefaultView = false;
                field.DisplayName = "File Category";
                field.ExtendedType = FieldType.Text;
                field.Name = "File Category";
                field.Required = false;
                field.Richtext = false;
                contentType.Fields.Add(field);

                ContentTypes.Serialize(p.OutputDirectory + "\\Listtemplates.xml");
//                createdFiles.WriteLine("del " + p.OutputDirectory + "\\Listtemplates.xml");
            }
            catch (Exception Ex)
            {
                Logger.Log(ErrorLevel.Error, "Errors occurred when creating the ContentTypes.xml file. Error: " + Ex.Message);
            }

            #endregion
            return returnValue;

        }

        public bool Process(FileParams p)
        {
            bool returnValue = true;
            //Cleanup illegal characters
            p.DocumentLibraryName = StripCharacters(p.DocumentLibraryName);
            Import import = new Import();
            import.Settings = new Settings();
            import.Settings.Locale = System.Threading.Thread.CurrentThread.CurrentUICulture.LCID.ToString(CultureInfo.InvariantCulture);
            Root rootContainer = new Root();
            rootContainer.Name = StripCharacters(p.ManagedPath);
            rootContainer.DataType = p.ContainerType;
            import.RootContainers.Add(rootContainer);

            Site site = new Site();
            rootContainer.Sites.Add(site);
            site.Name = StripCharacters(p.SiteName);
            if (p.Owner.Length < 1)
            {
                WindowsPrincipal wp = new WindowsPrincipal(WindowsIdentity.GetCurrent());
                p.Owner = wp.Identity.Name;
            }

            site.Owner = p.Owner;
            site.DataType = p.ContainerType;
            site.Contact = p.Contact;

            DirectoryInfo dir = new DirectoryInfo(p.StartDirectory);
            if (p.StartDirectory != p.OutputDirectory)
            {
                Logger.Log(ErrorLevel.Error, "If the start directory is not the same as the output directory then the startdirectory needs to be specified as an UNC path");
                return false;
            }
            
            if (createdFiles == null)
            {
                createdFiles = new StreamWriter(p.OutputDirectory + "\\deletecreatedfiles.cmd");
            }

            EnumSecurity(site, dir);

            FileSystemInfo[] dirs;
            if (p.Mode == null)
            {
                p.Mode = "single";
            }

            switch (p.Mode.ToLower(CultureInfo.InvariantCulture))
            {
                case "single":
                    try
                    {
                        List list = new List();
                        list.DataType = ListType.Library;
                        list.Template = "Imported Documents";
                        list.Name = StripCharacters(p.DocumentLibraryName);

                        list.WebPart = true;
                        list.QuickLaunch = true;
                        list.BaseTemplate = "DocumentLibrary";

                        site.Lists.Add(list);
                        Logger.Log(ErrorLevel.Information, "Processing " + p.StartDirectory);
                        EnumDirectory(dir, list, p.OutputDirectory, "\\", p, true);
                    }
                    catch (IOException e)
                    {
                        Logger.Log(ErrorLevel.Error, "Unrecoverable exception occured, halting. Message is: " + e.Message);
                        return false;
                    }
                    break;
                case "multi":
                    dirs = dir.GetDirectories("*");

                    foreach (DirectoryInfo thisDir in dirs)
                    {
                        try
                        {
                            List list = new List();
                            list.DataType = ListType.Library;
                            list.Template = "Imported Documents";
                            list.Name = StripCharacters(thisDir.Name);
                            list.WebPart = true;
                            list.QuickLaunch = true;
                            list.BaseTemplate = "DocumentLibrary";
                            string saveTo = p.OutputDirectory.Trim("\\".ToCharArray()) + "\\" + StripCharacters(thisDir.Name);
                            string relativePath = "\\" + StripCharacters(thisDir.Name) + "\\";
                            Logger.Log(ErrorLevel.Information, "Processing " + StripCharacters(thisDir.Name));
                            site.Lists.Add(list);
                            EnumDirectory(thisDir, list, p.OutputDirectory.TrimEnd("\\".ToCharArray()), relativePath, p, true);
                        }
                        catch (IOException e)
                        {
                            Logger.Log(ErrorLevel.Warning, "Could not export folder, skipping. Message is: " + e.Message);
                        }
                    }
                    break;
                case "subsite":
                    EnumerateSubWebs(dir, site, p, "\\");
                    break;
                default:
                    Logger.Log(ErrorLevel.Error, "Incorrect -mode passed, exiting!");
                    return false;
            }
            try
            {
                if (import.Validate())
                {
                    import.Serialize(p.OutputDirectory + "\\struct.xml");
                    createdFiles.WriteLine("del " + p.OutputDirectory + "\\struct.xml");
                }
                else
                    Logger.Log(ErrorLevel.Error, import.LastError);
            }
            catch (SPExportException ex)
            {
                Logger.Log(ErrorLevel.Error, "Serialization error: " + ex.Message);
            }

            Logger.Log(ErrorLevel.Information, "Nr of files exported = " + nrOfFilesExported.ToString());
            Logger.Log(ErrorLevel.Information, "Done");
            createdFiles.WriteLine("del " + p.OutputDirectory + "\\Manifest.xml");
            createdFiles.WriteLine("del " + p.OutputDirectory + "\\Requirements.xml");
            createdFiles.WriteLine("del " + p.OutputDirectory + "\\RootObjectMap.xml");
            createdFiles.WriteLine("del " + p.OutputDirectory + "\\SystemData.xml");
            createdFiles.WriteLine("del " + p.OutputDirectory + "\\UserGroup.xml");
            createdFiles.WriteLine("del " + p.OutputDirectory + "\\ViewFormsList.xml");
            createdFiles.WriteLine("del " + p.OutputDirectory + "\\setup.inf");
            createdFiles.WriteLine("del " + p.OutputDirectory + "\\setup.rpt");
            createdFiles.WriteLine("del " + p.OutputDirectory + "\\deletecreatedfiles.cmd");


            createdFiles.Flush();
            createdFiles.Close();

            return returnValue;

        }

        public void Compress(FileParams p)
        {
            Logger.Log(ErrorLevel.Progress, "Starting the file compression");
            //SharePoint.ImportExport.Compression.Compressor comp = new SharePoint.ImportExport.Compression.Compressor();

            //if (comp.Compress(p.OutputDirectory + "\\struct.xml", "packed"))
            //{
            //    Logger.Log(ErrorLevel.Progress, "Compression was successful");
            
            //    File.Delete(p.CompressedDirectory + "\\exporteddata.cab");
            //    File.Move(p.OutputDirectory + "\\packed\\exporteddata.cab", p.CompressedDirectory + "\\exporteddata.cab");

            //    Logger.Log(ErrorLevel.Progress, "Export file copied to: " + p.CompressedDirectory + "\\exporteddata.cab");

            //    Logger.Log(ErrorLevel.Progress, "Cleaning up temporary files");

            //    comp.CleanUp(p.OutputDirectory + "\\struct.xml",true);
            //    File.Delete(p.OutputDirectory + "\\packed\\items.xml");
            //    Directory.Delete(p.OutputDirectory + "\\packed");

            //    Process proc = new Process();
            //    ProcessStartInfo pInfo = new ProcessStartInfo("cmd.exe");
            //    pInfo.WorkingDirectory = p.OutputDirectory;
            //    pInfo.Arguments = "/c " + p.OutputDirectory + "\\deletecreatedfiles.cmd";
            //    pInfo.WindowStyle = ProcessWindowStyle.Hidden;
            //    proc.StartInfo = pInfo;
            //    proc.Start();
            //    proc.WaitForExit();
            //    int code = proc.ExitCode;

            //    Logger.Log(ErrorLevel.Progress, "Done");
           
            //}

        }

        #region Strip illigal characters from strings
        private static string StripCharacters(string String)
        {
            //replace following characters with _ : /\:*?"<>|#{}%&~ 
            //Will replace this function with a cleaner one later on...
            String = String.Replace(":", "_");
            String = String.Replace(";", "_");
            String = String.Replace("/", "_");
            String = String.Replace("\\", "_");
            String = String.Replace("*", "_");
            String = String.Replace("?", "_");
            String = String.Replace("\"", "_");
            String = String.Replace("<", "_");
            String = String.Replace(">", "_");
            String = String.Replace(">", "_");
            String = String.Replace("|", "_");
            String = String.Replace("#", "_");
            String = String.Replace("{", "_");
            String = String.Replace("}", "_");
            String = String.Replace("%", "_");
            String = String.Replace("&", "_");
            String = String.Replace("~", "_");
            String = String.Replace("=", "_");
            return String;
        }
        #endregion

        #region Enumerate Directories as Subwebs
        private void EnumerateSubWebs(DirectoryInfo dir, Site site, FileParams p, string relativePath)
        {
            if (dir.GetFiles().Length > 0)
            {
                List library = new List();
                library.DataType = ListType.Library;
                library.Template = "Imported Documents";
                library.Name = "Imported Documents";
                library.WebPart = true;
                library.QuickLaunch = true;
                library.BaseTemplate = "DocumentLibrary";
                string saveTo = p.OutputDirectory.Trim("\\".ToCharArray()) + "\\" + relativePath;
                Logger.Log(ErrorLevel.Information, "Processing " + StripCharacters(dir.Name));
                site.Lists.Add(library);
                EnumDirectory(dir, library, p.OutputDirectory.TrimEnd("\\".ToCharArray()), relativePath, p, false);
            }
            FileSystemInfo[] dirs = dir.GetDirectories("*");

            if (dirs.Length > 0)
            {
                if (p.ContainerType == SiteType.Site)
                {
                    List linksList = new List();
                    site.Lists.Add(linksList);
                    linksList.Template = "Links";
                    linksList.Name = "Created Subsites";
                    linksList.WebPart = true;
                    linksList.InheritPermissions = true;
                    linksList.RelativeitemXML = relativePath + "ListItems.xml";
                    linksList.DataType = ListType.Library;
                    ListOfItems list = new ListOfItems();
                    ItemsOfContentType profile = new ItemsOfContentType();
                    list.ItemsOfContentType.Add(profile);
                    profile.Name = "Links";

                    foreach (DirectoryInfo thisDir in dirs)
                    {
                        Item item = new Item();
                        profile.Items.Add(item);
                        ItemProperty field = new ItemProperty();
                        field.Name = "URL";
                        field.DataType = FieldType.Text;
                        field.Value = "/" + StripCharacters(thisDir.Name) + ", " + StripCharacters(thisDir.Name);
                        item.Fields.Add(field);

                        Site subSite = new Site();
                        EnumSecurity(subSite, thisDir);
                        site.SubSites.Add(subSite);
                        subSite.Name = StripCharacters(thisDir.Name);
                        subSite.Owner = p.Owner;
                        subSite.DataType = p.ContainerType;
                        subSite.Contact = p.Contact;
                        EnumerateSubWebs(thisDir, subSite, p, relativePath + StripCharacters(thisDir.Name) + "\\");
                    }

                    if (!Directory.Exists(p.OutputDirectory.TrimEnd("\\".ToCharArray()) + relativePath))
                    {
                        Directory.CreateDirectory(p.OutputDirectory.TrimEnd("\\".ToCharArray()) + relativePath);
                    }

                    list.Serialize(p.OutputDirectory.TrimEnd("\\".ToCharArray()) + linksList.RelativeitemXML);
                    createdFiles.WriteLine("del " + p.OutputDirectory.TrimEnd("\\".ToCharArray()) + linksList.RelativeitemXML);
                }
                else
                {
                    foreach (DirectoryInfo thisDir in dirs)
                    {
                        Site subSite = new Site();
                        EnumSecurity(subSite, thisDir);
                        site.SubSites.Add(subSite);
                        subSite.Name = StripCharacters(thisDir.Name);
                        subSite.Owner = p.Owner;
                        subSite.DataType = p.ContainerType;
                        subSite.Contact = p.Contact;
                        EnumerateSubWebs(thisDir, subSite, p, relativePath + StripCharacters(thisDir.Name) + "\\");
                    }

                    if (!Directory.Exists(p.OutputDirectory.TrimEnd("\\".ToCharArray()) + relativePath))
                    {
                        Directory.CreateDirectory(p.OutputDirectory.TrimEnd("\\".ToCharArray()) + relativePath);
                    }
                }
            }

        }
        #endregion

        #region Enumerate Security
        public static void EnumSecurity(Site site, FileSystemInfo dirInfo)
        {
            Logger.Log(ErrorLevel.Information, "Processing security for folder: " + dirInfo.FullName);
            string Share = dirInfo.FullName;
            SecurityDescriptor secDesc = null;
            try
            {
                secDesc = SecurityDescriptor.GetNamedSecurityInfo(Share, SE_OBJECT_TYPE.SE_FILE_OBJECT, SECURITY_INFORMATION.DACL_SECURITY_INFORMATION);
            }
            catch
            {
                Logger.Log(ErrorLevel.Information, "\t No security found.");
                site.InheritPermissions = true;
                return;
            }
            if (secDesc != null)
            {
                using (secDesc)
                {
                    UserInfo userInfoList = new UserInfo();
                    foreach (Ace ace in secDesc.Dacl)
                    {
                        try
                        {
                            if (ace.Flags.CompareTo(AceFlags.INHERITED_ACE) >= 0)
                            {
                                site.InheritPermissions = true;
                                Logger.Log(ErrorLevel.Information, "Permissions set to inherit");
                                break;
                            }
                            if (ace.Sid.DomainName.ToLower(CultureInfo.InvariantCulture) == "builtin" || ace.Sid.DomainName.ToLower(CultureInfo.InvariantCulture) == "nt authority")
                            {
                                break;
                            }
                            site.InheritPermissions = false;
                            Logger.Log(ErrorLevel.Information, "Adding: " + ace.Sid.AccountName);
                            User user = new User();
                            user.Account = ace.Sid.CanonicalName;
                            user.Name = ace.Sid.AccountName;
                            DirectoryAccessType access = (DirectoryAccessType)ace.AccessType;

                            /*
                                int i = access.CompareTo(DirectoryAccessType.FILE_GENERIC_READ);
                                int x = access.CompareTo(DirectoryAccessType.FILE_GENERIC_WRITE);
                                int y = access.CompareTo(DirectoryAccessType.FILE_ALL_ACCESS);
                                string s = access.ToString();
                                */

                            if (access.CompareTo(DirectoryAccessType.FILE_GENERIC_READ) >= 0)
                            {
                                user.Role = "Reader";
                            }

                            if (access.CompareTo(DirectoryAccessType.FILE_GENERIC_WRITE) >= 0)
                            {
                                user.Role = "Contributor";
                            }

                            if (access.CompareTo(DirectoryAccessType.FILE_ALL_ACCESS) >= 0)
                            {
                                user.Role = "Administrator";
                            }
                            Logger.Log(ErrorLevel.Information, "Role: " + user.Role + " granted");
                            userInfoList.Users.Add(user);
                        }
                        catch (IOException e)
                        {
                            Logger.Log(ErrorLevel.Error, "Error processing user: " + ace.Sid.AccountName + ". Error was: " + e.Message);
                        }
                    }
                    site.UserInfo = userInfoList;
                }
            }
            else
            {
                Logger.Log(ErrorLevel.Warning, "No security information found? Is the share in Everyone full permissions?");
            }
            return;
        }
        #endregion

        #region Enumerate Directory Contents
        public void EnumDirectory(DirectoryInfo dir, List list, string saveTo, string relativePath, FileParams p, bool recursive)
        {
            try
            {
                OleDocumentProperties reader = new OleDocumentProperties();
                ListOfItems itemslist = new ListOfItems();
                ItemsOfContentType profile = new ItemsOfContentType();
                itemslist.ItemsOfContentType.Add(profile);
                profile.Name = "Imported Documents";

                string sFileName;
                sFileName = saveTo + relativePath + "\\items.xml";
                list.RelativeitemXML = relativePath + "items.xml";

                foreach (FileInfo f in dir.GetFiles("*.*"))
                {
                    bool bContinue = true;
                    switch (f.Name.ToLower())
                    {
                        case "struct.xml":
                        case "items.xml":
                        case "ContentTypes.xml":
                        case "exportsettings.xml":
                        case "manifest.xml":
                        case "requirements.xml":
                        case "rootobjectmap.xml":
                        case "systemdata.xml":
                        case "usergroup.xml":
                        case "viewformslist.xml":
                            bContinue = false;
                            break;
                        default:
                            break;
                    }

                    if (!bContinue)
                    {
                        continue;
                    }

                    if (p.DateSpecified)
                    {
                        if (f.LastWriteTimeUtc < p.DateTimeThreshhold)
                        {
                            continue;
                        }
                    }

                    bContinue = true;
                    if (p.Exclusions != null)
                    {
                        if (p.Exclusions.Count > 0)
                        {
                            foreach (string excludedextension in p.Exclusions)
                            {
                                if (excludedextension == f.Extension)
                                {
                                    bContinue = false;
                                    break;
                                }
                            }
                        }
                    }

                    if (!bContinue)
                    {
                        continue;
                    }

                    if (p.Inclusions != null)
                    {

                        if (p.Inclusions.Count > 0)
                        {
                            bContinue = false;
                            foreach (string extension in p.Inclusions)
                            {
                                if (extension == f.Extension)
                                {
                                    bContinue = true;
                                    break;
                                }
                            }
                        }
                        else
                        {
                            bContinue = true;
                        }
                    }

                    if (!bContinue)
                    {
                        continue;
                    }

                    Logger.Log(ErrorLevel.Information, "\tProcessing " + StripCharacters(f.Name));
                    Item item = new Item();
                    item.DisplayName = StripCharacters(f.Name);
                    FileInfo fi = new FileInfo(f.FullName);
                    item.FileName = f.Name;
                    nrOfFilesExported++;
                    ItemProperty ItemProperty = new ItemProperty("created", FieldType.DateTime, f.CreationTimeUtc.ToString(CultureInfo.InvariantCulture));
                    item.Fields.Add(ItemProperty);

                    ItemProperty = new ItemProperty("modified", FieldType.DateTime, f.LastWriteTimeUtc.ToString(CultureInfo.InvariantCulture));
                    item.Fields.Add(ItemProperty);
                    bool fileOpened = false;

                    try
                    {
                        reader.Open(f.FullName, true, DSOFile.dsoFileOpenOptions.dsoOptionOnlyOpenOLEFiles);
                        fileOpened = true;
                        if (reader.SummaryProperties.Author != null)
                        {
                            ItemProperty = new ItemProperty("File Author", FieldType.Text, reader.SummaryProperties.Author);
                            item.Fields.Add(ItemProperty);
                        }

                        if (reader.SummaryProperties.Title != null)
                        {
                            ItemProperty = new ItemProperty("Title", FieldType.Text, reader.SummaryProperties.Title);
                            item.Fields.Add(ItemProperty);
                        }
                        if (reader.SummaryProperties.Comments != null)
                        {
                            ItemProperty = new ItemProperty("File Description", FieldType.Text, reader.SummaryProperties.Comments);
                            item.Fields.Add(ItemProperty);
                        }

                        if (reader.SummaryProperties.Category != null)
                        {
                            ItemProperty = new ItemProperty("File Category", FieldType.Text, reader.SummaryProperties.Category);
                            item.Fields.Add(ItemProperty);
                        }
                        reader.Close(false);
                        fileOpened = false;

                    }
                    catch (COMException Ex)
                    {
                        if (fileOpened)
                            reader.Close(false);
                        fileOpened = false;
                        if (Ex.ErrorCode != -2147217148)
                        {
                            Logger.Log(ErrorLevel.Warning, "Could not read the office properties from " + f.FullName + ".Error :" + Ex.Message);
                        }
                        Logger.Log(ErrorLevel.File, "Could not read the office properties from " + f.FullName + ".Error :" + Ex.Message);
                    }

                    profile.Items.Add(item);
                }

                try
                {
                    if (!Directory.Exists(saveTo + relativePath))
                        Directory.CreateDirectory(saveTo + relativePath);
                    itemslist.Serialize(saveTo + list.RelativeitemXML);
                    createdFiles.WriteLine("del " + saveTo + list.RelativeitemXML);
                }
                catch (SPExportException ex)
                {
                    Logger.Log(ErrorLevel.Error, "Serialization error: " + ex.Message);
                }

                if (recursive)
                {
                    FileSystemInfo[] dirs = dir.GetDirectories("*");

                    foreach (DirectoryInfo thisDir in dirs)
                    {
                        List folder = new List();
                        folder.DataType = ListType.Folder;
                        folder.Template = list.Template;
                        folder.BaseTemplate = list.BaseTemplate;
                        folder.Name = StripCharacters(thisDir.Name);
                        Logger.Log(ErrorLevel.Progress, "Processing " + StripCharacters(thisDir.Name));
                        list.Folders.Add(folder);

                        EnumDirectory(thisDir, folder, saveTo, relativePath + thisDir.Name + "\\", p, true);
                    }
                }
            }

            catch (IOException Ex)
            {
                Logger.Log(ErrorLevel.Error, "Error when processing the directory: " + dir.Name + ".Error: " + Ex.Message);
            }
            catch (COMException ComEx)
            {
                Logger.Log(ErrorLevel.Error, "Error when processing the directory: " + dir.Name + ".Error: " + ComEx.Message);
            }
        }
        #endregion
    }

}

